"use strict";

define('config', {
    api: location.href.startsWith("http://localhost:") ? "http://localhost:8080/rsms" : "https://api.cjprods.org/rsms"
});

define('url', {
    // break url into { base, arg, anchor }
    //
    // base   will be kept as is (no decodeURI being done)
    // arg    will be kept as is (no decodeURI being done)
    // anchor will be decodeURI.
    //
    // - "base"                     -> { "base", undefined, undefined }
    // - "base?arg"                 -> { "base", "?arg", undefined }
    // - "base#anchor"              -> { "base", undefined, "#anchor" }
    // - "base?arg#anchor"          -> { "base", "?arg", "#anchor" }
    // - "base#anchor?still#anchor" -> { "base", undefined, "#anchor?still#anchor" }

    split: function split(url) {
        var result = {};

        // anchor
        var anchor_at = url.indexOf("#");
        if (anchor_at !== -1) {
            result.anchor = decodeURI(url.substr(anchor_at));
            url = url.substr(0, anchor_at);
        }

        // arg
        var arg_at = url.indexOf("?");
        if (arg_at !== -1) {
            result.arg = url.substr(arg_at);
            url = url.substr(0, arg_at);
        }

        // base
        result.base = url;

        return result;
    },

    // break url arg part into object (key-value pair)
    // will decodeURIComponent
    // "?a=1%200&b=11" -> { a: "1 0", b: "11" }
    parse_arg: function parse_arg(arg) {
        if (arg == null) return {};

        var args = {};
        arg.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (_, name, value) {
            return args[decodeURIComponent(name)] = decodeURIComponent(value);
        });
        return args;
    },
    build_arg: function build_arg(args) {
        var arg = [];
        for (var name in args) {
            arg.push(encodeURIComponent(name) + "=" + encodeURIComponent(args[name]));
        }if (arg.length === 0) return "";
        return "?" + arg.join("&");
    },

    // parse url into base, arg, anchor args
    parse: function parse(url) {
        var result = this.split(url);
        result.args = this.parse_arg(result.arg);
        return result;
    },

    // build url from { base, arg, anchor }
    // { "http://example.com/", "?hi=10&yes=%2F", "#hello/" }
    //   -> "http://example.com/?hi=10&yes=%2F#hello%2F"
    // { "http://example.com/", { hi: 10, yes: "%2F" }, "#hello/" }
    //   -> "http://example.com/?hi=10&yes=%252F#hello%2F"
    build: function build(base, arg, anchor) {
        if (arg == null) arg = "";
        if (typeof arg !== 'string') arg = this.build_arg(arg);

        if (anchor == null) anchor = "";
        anchor = encodeURI(anchor);

        return base + arg + anchor;
    }
});

define('base64', {
    encode: function encode(x) {
        var x = btoa(unescape(encodeURIComponent(x))); // UTF-16 -> UTF-8-with-wchar -> base64
        x = x.replace(/[+/]/g, function (x) {
            return ({
                "+": "-",
                "/": "_"
            })[x];
        });

        var npad = 0;
        while (x.endsWith("=")) {
            npad++;
            x = x.substr(0, x.length - 1);
        }
        return x + npad;
    },
    decode: function decode(x) {
        var npad = parseInt(x.substr(x.length - 1));
        x = x.substr(0, x.length - 1) + "=".repeat(npad);

        x = x.replace(/[-_]/g, function (x) {
            return ({
                "-": "+",
                "_": "/"
            })[x];
        });
        return decodeURIComponent(escape(atob(x))); // base64 -> UTF-8-with-wchar -> UTF-16
    }
});

define('ajax', ['url'], function (url) {
    return {
        json: function json(post, base, args, ok, fail) {
            // normalize arguments
            if (ok == null) ok = function () {};
            if (fail == null) fail = function () {};

            // prepare callbacks
            var req = new XMLHttpRequest();
            req.onload = function () {
                var json = req.response;
                if (json == null) json = { error: "internal error" };
                if (json.error != null) fail(json.error);else ok(json.result);
            };
            req.onabort = function () {
                return fail("abort");
            };
            req.onerror = function () {
                return fail("error");
            };

            // fire!
            req.responseType = 'json';
            if (post) {
                req.open('POST', base);
                req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                req.send(url.build_arg(args).substr(1)); // arg always begins with '?' or empty string
            } else {
                    req.open('GET', url.build(base, args));
                    req.send();
                }
        },
        get_json: function get_json(base, args, ok, fail) {
            this.json(false, base, args, ok, fail);
        },
        post_json: function post_json(base, args, ok, fail) {
            this.json(true, base, args, ok, fail);
        }
    };
});

define('api', ['config', 'url', 'base64', 'ajax'], function (config, url, base64, ajax) {
    return function () {
        var base = config.api;
        if (base == null) return;
        if (base.endsWith("/")) base = base.substr(0, base.length - 1);

        //----------------------------------------------------------------------
        // login and authentication

        var login = function login() {
            var loc = url.parse(location.href);
            delete loc.args.name;
            delete loc.args.username;
            var callback = url.build(loc.base, loc.args);
            location.href = url.build(base + "/login", { redirect: callback });
        };

        var roles = ['信息部管理员', '销售部业务员', '销售部经理', '售后部业务员', '售后部经理', '售后部信息员'];

        var parse_token = function parse_token(token) {
            // parse
            var shards = token.split('.');
            if (shards.length != 2) return;
            var raw_token = JSON.parse(base64.decode(shards[0]));

            // simple validation
            if (roles.indexOf(raw_token.role) === -1) return;
            if (raw_token.seq == null) return;

            return raw_token;
        };

        var init = function init() {
            var info = url.parse(location.href);
            var args = info.args;
            var token = info.anchor;
            if (token == null) return;
            if (args.name == null) return;
            if (args.username == null) return;

            token = token.substr(1);
            var raw_token = parse_token(token);
            if (raw_token == null) return;

            return {
                base: base,
                login: login,
                roles: roles,
                token: token,
                name: args.name,
                username: args.username,
                role: raw_token.role
            };
        };

        // check login
        var api = init();
        if (api == null) return login();

        //----------------------------------------------------------------------
        // APIs

        var zip_args = function zip_args(arg_names, args) {
            var result = {};
            arg_names.forEach(function (name, i) {
                var arg = args[i];
                if (arg == null) throw "bad api call: null not allowed in (" + name + ") of (" + arg_names + ")";
                result[name] = arg;
            });
            return result;
        };

        var make_scalar_api_invoker = function make_scalar_api_invoker(post, addr, arg_names) {
            return function () {
                for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
                    args[_key] = arguments[_key];
                }

                var remain = args.length - arg_names.length;
                if (remain !== 1 && remain !== 2) throw "bad api call: expect " + (arg_names.length + 1) + " or " + (arg_names.length + 2) + " arguments (" + arg_names + ",callback,[update]), but " + args.length + " were given.";

                var callback = args[arg_names.length];
                if (typeof callback !== 'function') throw "bad api call: callback must be a function";
                var update = args[arg_names.length + 1];
                if (update == null) update = function () {};
                if (typeof update !== 'function') throw "bad api call: optional 'update' must be a function";

                var ok = function ok() {
                    return callback(null);
                };
                var fail = function fail(error) {
                    if (error === 'denied') return login();

                    console.error(error);
                    if (error === 'bad argument: not unique') return callback("不唯一");
                    if (error.endsWith('outdated')) {
                        update();
                        return callback("数据已过期，没有进行任何操作");
                    }
                    if (error.startsWith('bad argument:')) return callback("参数错误：" + error);
                    callback("服务器错误");
                };

                var zargs = zip_args(arg_names, args);
                zargs.token = api.token;
                ajax.json(post, addr, zargs, ok, fail);
            };
        };

        var make_list_api_invoker = function make_list_api_invoker(addr, arg_names) {
            return function () {
                for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
                    args[_key2] = arguments[_key2];
                }

                var zargs = zip_args(arg_names, args);
                zargs.token = api.token;

                return function () {
                    var after = -1;
                    var end = false;

                    return function (callback) {
                        if (typeof callback !== 'function') throw "callback must be a function";

                        var ok = function ok(list) {
                            if (list.length === 0) end = true;else after = list[list.length - 1].id;
                            callback(list);
                        };
                        var fail = function fail(error) {
                            if (error === 'denied') return login();
                            console.error(error);
                            callback(null);
                        };

                        if (end) setTimeout(function () {
                            return callback([]);
                        }); // setTimeout to emulate promise
                        else {
                                zargs.after = after;
                                ajax.get_json(addr, zargs, ok, fail);
                            }
                    };
                };
            };
        };

        var make_api_invoker = function make_api_invoker(spec) {
            var arg_names = spec.split(/\s+/);
            var addr = arg_names.shift();

            var invoker;
            if (addr.startsWith('@')) {
                addr = addr.substr(1);
                invoker = make_list_api_invoker(base + "/api" + addr, arg_names);
            } else {
                var post = false;
                if (addr.startsWith('!')) {
                    post = true;
                    addr = addr.substr(1);
                }
                invoker = make_scalar_api_invoker(post, base + "/api" + addr, arg_names);
            }

            var name = addr.substr(1).split('/').join('_');
            return { name: name, invoker: invoker };
        };

        var populate_api = function populate_api(specs) {
            return specs.map(make_api_invoker).forEach(function (x) {
                return api[x.name] = x.invoker;
            });
        };

        populate_api(['/user/revoke/self', '/user/leave user', '/user/validate', '/user/password user new', '/user/password/self new', '/user/role user new', '/user/name user new', '!/user/add username password role name', '@/user/list', '!/sale/add purpose budget', '/sale/cancel sale', '/sale/approve sale', '!/sale/reject sale reason', '@/sale/list', '@/sale/list/self', '!/sale/activity/add sale activity cost', '@/sale/activity/list sale', '@/sale/activity/list/self sale', '!/ticket/add complaint customer contact', '/ticket/assign ticket user', '@/ticket/list', '@/ticket/list/self', '@/ticket/list/latest', '@/ticket/list/free', '/ticket/done assignment', '!/ticket/fail assignment detail', '!/ticket/waive assignment detail']);

        return api;
    };
});

define('app', [], function () {
    return function (angular, api) {
        var app = angular.module('rsms', ['ngMaterial']);

        app.config(function ($mdThemingProvider, $mdIconProvider) {
            $mdThemingProvider.theme('default').primaryPalette('blue');

            // load icons
            ['home', 'menu', 'more-vert', 'account-switch', 'account-alert', 'account-key', 'account-multiple', 'account-convert', 'account-clipboard', 'account-network', 'back', 'refresh', 'plus', 'check', 'close', 'cancel', 'receipt', 'trending-up', 'comment', 'comment-more', 'motorbike', 'cash', 'list-ordered'].forEach(function (icon) {
                return $mdIconProvider.icon(icon, './icons/' + icon + '.svg');
            });
        });

        app.controller('app-controller', function ($mdSidenav, $mdMedia, $scope, $mdComponentRegistry, $mdToast, $mdDialog) {
            var _ = this;

            function popup(msg, delay) {
                var toast = $mdToast.simple().textContent(msg).position('top right');
                if (delay != null) toast = toast.hideDelay(delay);
                $mdToast.show(toast);
            }

            function prompt_dialog(info, open_from) {
                $mdDialog.show({
                    templateUrl: 'prompt-dialog',
                    clickOutsideToClose: true,
                    controller: 'prompt-dialog-controller as dialog',
                    openFrom: open_from,
                    locals: { info: info }
                });
            }

            _.busy = null;
            _.name = api.name;
            _.username = api.username;
            _.role = api.role;
            _.api_base = api.base;
            _.roles = api.roles;

            _.set_busy = function (busy) {
                _.busy = busy == null ? "" : busy;
            };
            _.set_idle = function () {
                _.busy = null;
            };

            _.toolbar_show_icons = function () {
                return $mdMedia('gt-md');
            };
            _.menu_lock_open = function () {
                var big = $mdMedia('gt-xs');
                if (big && _.menu_opened()) _.close_menu();
                return big;
            };

            _.menu_opened = function () {
                return false;
            };
            _.open_menu = function () {};
            _.close_menu = function () {};

            $mdComponentRegistry.when('left').then(function (nav) {
                _.menu_opened = function () {
                    return nav.isOpen();
                };
                _.open_menu = function () {
                    nav.open();
                };
                _.close_menu = function () {
                    nav.close();
                };
            });

            _.pages = [{
                name: '主页',
                icon: 'home',
                view: 'home'
            }, {
                name: '用户',
                icon: 'account-multiple',
                view: 'list',
                role: '信息部管理员',
                list: api.user_list(),
                list_text: {
                    major: function major(item) {
                        return item.name;
                    },
                    desc: function desc(item) {
                        return item.role;
                    },
                    minor: function minor(item) {
                        return item.username;
                    },
                    menu: function menu(item) {
                        return "用户名：" + item.username;
                    }
                },
                add: {
                    inputs: [{
                        id: 'name',
                        label: "姓名",
                        type: "text",
                        icon: 'account-clipboard'
                    }, {
                        id: 'role',
                        label: "角色",
                        selections: api.roles,
                        icon: 'account-convert'
                    }, {
                        id: 'username',
                        label: "用户名",
                        type: "text",
                        icon: 'account-multiple'
                    }, {
                        id: 'password',
                        label: "密码",
                        type: "password",
                        icon: 'account-key'
                    }],
                    action: function action(values, close) {
                        _.set_busy();
                        api.user_add(values.username, values.password, values.role, values.name, function (err) {
                            if (err != null) {
                                _.set_idle();
                                popup(err);
                            } else {
                                _.set_idle();
                                popup("用户 " + values.name + " (" + values.username + ") 添加成功");
                                close();
                                _.list_refresh();
                            }
                            $scope.$apply();
                        });
                    }
                },

                menu: [{
                    name: '修改姓名',
                    icon: 'account-clipboard',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            label: '新姓名',
                            value: item.name,
                            icon: menu.icon,
                            text: item.name + "(" + item.username + ")",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(name) {
                                if (item.name === name) return;
                                _.set_busy();
                                api.user_name(item.id, name, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        if (item.username === _.username) {
                                            _.set_busy("姓名修改成功。请重新登录。");
                                            setTimeout(api.login, 4000);
                                        } else {
                                            _.set_idle();
                                            item.name = name;
                                            popup(item.username + " 的姓名已修改为：" + name);
                                        }
                                    }
                                    $scope.$apply();
                                });
                            }
                        }, event.target);
                    }
                }, {
                    name: '修改密码',
                    icon: 'account-key',
                    style: 'md-primary',
                    disabled: 'item.username === app.username',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            label: '新密码',
                            type: 'password',
                            icon: menu.icon,
                            text: item.name + "(" + item.username + ")",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(pwd) {
                                _.set_busy();
                                api.user_password(item.id, pwd, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup(item.username + " (" + item.name + ") 的密码修改成功");
                                    }
                                    $scope.$apply();
                                });
                            }
                        }, event.target);
                    }
                }, {
                    name: '修改角色',
                    icon: 'account-convert',
                    style: 'md-warn',
                    disabled: 'item.username === app.username',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            selections: _.roles,
                            value: item.role,
                            icon: menu.icon,
                            text: item.name + "(" + item.username + ")",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(role) {
                                if (item.role === role) return;
                                _.set_busy();
                                api.user_role(item.id, role, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        item.role = role;
                                        popup(item.username + " (" + item.name + ") 现在是 " + role);
                                    }
                                    $scope.$apply();
                                });
                            }
                        }, event.target);
                    }
                }, {
                    name: '离职',
                    icon: 'cancel',
                    style: 'md-accent',
                    disabled: 'item.username === app.username',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: "确定将 " + item.name + "(" + item.username + ") 标记为离职？此操作将无法撤销。",
                            action_text: menu.name,
                            action: function action() {
                                _.set_busy();
                                api.user_leave(item.id, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup(item.name + "(" + item.username + ") 已离职");
                                        _.list_refresh();
                                    }
                                    $scope.$apply();
                                });
                            }
                        }, event.target);
                    }
                }]
            }, {
                name: '业务',
                icon: 'trending-up',
                view: 'list',
                role: '销售部经理',
                list: api.sale_list(),
                list_text: {
                    major: function major(item) {
                        return "￥" + item.budget;
                    },
                    desc: function desc(item) {
                        return item.approval == null || item.approval === "" ? item.purpose : item.approval;
                    },
                    minor: function minor(item) {
                        return item.approval == null ? "" : item.approval === "" ? "已批准" : "已拒绝";
                    },
                    menu: function menu(item) {
                        return item.applicant + " 申请于 " + item.date + (item.approval == null || item.approval === "" ? "" : "，" + item.purpose);
                    },
                    style: function style(item) {
                        return item.approval == null ? "" : item.approval === "" ? "color-yes" : "color-no";
                    }
                },
                menu: [{
                    name: '批准',
                    icon: 'check',
                    style: 'md-primary',
                    disabled: 'item.approval != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: "确定？",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action() {
                                _.set_busy();
                                api.sale_approve(item.id, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup("已批准 " + item.applicant + " 的 ￥" + item.budget + " 业务");
                                        item.approval = "";
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }, {
                    name: '拒绝',
                    icon: 'close',
                    style: 'md-accent',
                    disabled: 'item.approval != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: item.applicant + " 的 ￥" + item.budget + " 的业务：" + item.purpose,
                            label: "理由",
                            icon: 'comment',
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(reason) {
                                _.set_busy();
                                api.sale_reject(item.id, reason, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup("拒绝了 " + item.applicant + " 的 ￥" + item.budget + " 业务：" + reason);
                                        item.approval = reason;
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }, {
                    name: '活动记录',
                    icon: 'list-ordered',
                    disabled: 'item.approval !== ""',
                    click: function click(item) {
                        var sale = item.id;
                        _.page_select({
                            name: "活动",
                            view: 'list',
                            list: api.sale_activity_list(sale),
                            list_text: {
                                major: function major(item) {
                                    return "￥" + item.cost;
                                },
                                desc: function desc(item) {
                                    return item.activity;
                                },
                                minor: function minor(item) {
                                    return '';
                                },
                                menu: function menu(item) {
                                    return "添加于 " + item.date;
                                }
                            },
                            back: true
                        });
                    }
                }]
            }, {
                name: '业务',
                icon: 'trending-up',
                view: 'list',
                role: '销售部业务员',
                list: api.sale_list_self(),
                list_text: {
                    major: function major(item) {
                        return item.approval == null ? "待审批" : item.approval === "" ? "已批准" : "被拒绝";
                    },
                    desc: function desc(item) {
                        return item.approval == null || item.approval === "" ? item.purpose : item.approval;
                    },
                    minor: function minor(item) {
                        return "￥" + item.budget;
                    },
                    menu: function menu(item) {
                        return "申请于 " + item.date + (item.approval == null || item.approval === "" ? "" : "，" + item.purpose);
                    },
                    style: function style(item) {
                        return item.approval == null ? "" : item.approval === "" ? "color-yes" : "color-no";
                    }
                },
                add: {
                    inputs: [{
                        id: 'budget',
                        label: '预算（人民币）',
                        type: 'number',
                        icon: 'cash',
                        min: 0,
                        max: 99999999.99,
                        step: 0.01
                    }, {
                        id: 'purpose',
                        label: '梗概',
                        type: 'text',
                        icon: 'comment'
                    }],
                    action: function action(values, close) {
                        _.set_busy();
                        api.sale_add(values.purpose, values.budget, function (err) {
                            if (err != null) {
                                _.set_idle();
                                popup(err);
                            } else {
                                _.set_idle();
                                popup("业务添加成功");
                                close();
                                _.list_refresh();
                            }
                            $scope.$apply();
                        });
                    }
                },
                menu: [{
                    name: '活动记录',
                    icon: 'list-ordered',
                    disabled: 'item.approval !== ""',
                    click: function click(item) {
                        var sale = item.id;
                        _.page_select({
                            name: "活动",
                            view: 'list',
                            list: api.sale_activity_list_self(sale),
                            list_text: {
                                major: function major(item) {
                                    return "￥" + item.cost;
                                },
                                desc: function desc(item) {
                                    return item.activity;
                                },
                                minor: function minor(item) {
                                    return '';
                                },
                                menu: function menu(item) {
                                    return "添加于 " + item.date;
                                }
                            },
                            back: true,
                            add: {
                                inputs: [{
                                    id: 'cost',
                                    label: '花费（人民币）',
                                    type: 'number',
                                    icon: 'cash',
                                    min: 0,
                                    max: 99999999.99,
                                    step: 0.01
                                }, {
                                    id: 'activity',
                                    label: '梗概',
                                    type: 'text',
                                    icon: 'comment'
                                }],
                                action: function action(values, close) {
                                    _.set_busy();
                                    api.sale_activity_add(sale, values.activity, values.cost, function (err) {
                                        if (err != null) {
                                            _.set_idle();
                                            popup(err);
                                        } else {
                                            _.set_idle();
                                            popup("活动添加成功");
                                            close();
                                            _.list_refresh();
                                        }
                                        $scope.$apply();
                                    });
                                }
                            }
                        });
                    }
                }, {
                    name: '取消申请',
                    icon: 'cancel',
                    style: 'md-accent',
                    disabled: 'item.approval != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: "确定？",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action() {
                                _.set_busy();
                                api.sale_cancel(item.id, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup("￥" + item.budget + " 的业务已取消：" + item.purpose);
                                        item.approval = '取消';
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }]
            }, {
                name: '工单',
                icon: 'receipt',
                view: 'list',
                role: '售后部经理',
                list: api.ticket_list(),
                list_text: {
                    major: function major(item) {
                        return item.customer;
                    },
                    desc: function desc(item) {
                        return item.complaint;
                    },
                    minor: function minor(item) {
                        return item.assignee == null ? "未指派" : item.assignee + (item.result == null ? "" : item.result);
                    },
                    menu: function menu(item) {
                        return item.detail;
                    },
                    style: function style(item) {
                        return item.result == null ? '' : item.result === '成功' ? 'color-yes' : item.result === '失败' ? 'color-no2' : 'color-no';
                    }
                }
            }, {
                name: '工单',
                icon: 'receipt',
                view: 'list',
                role: '售后部业务员',
                list: api.ticket_list_self(),
                list_text: {
                    major: function major(item) {
                        return item.customer;
                    },
                    desc: function desc(item) {
                        return item.complaint;
                    },
                    minor: function minor(item) {
                        return item.result == null ? item.contact : item.result;
                    },
                    menu: function menu(item) {
                        return item.result == null ? item.contact : item.result + (item.result === '成功' ? '' : '：' + item.detail);
                    },
                    style: function style(item) {
                        return item.result == null ? '' : item.result === '成功' ? 'color-yes' : item.result === '失败' ? 'color-no2' : 'color-no';
                    }
                },
                menu: [{
                    name: '标记为成功',
                    icon: 'check',
                    style: 'md-primary',
                    disabled: 'item.result != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: "确定？",
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action() {
                                _.set_busy();
                                api.ticket_done(item.id, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup(item.customer + " 的工单处理成功");
                                        item.result = '成功';
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }, {
                    name: '标记为失败',
                    icon: 'close',
                    style: 'md-warn',
                    disabled: 'item.result != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: item.customer + "：" + item.complaint,
                            label: "说明",
                            icon: 'comment',
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(detail) {
                                _.set_busy();
                                api.ticket_fail(item.id, detail, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup(item.customer + " 的工单标记为失败：" + detail);
                                        item.result = '失败';
                                        item.detail = detail;
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }, {
                    name: '放弃该工单',
                    icon: 'cancel',
                    style: 'md-accent',
                    disabled: 'item.result != null',
                    click: function click(item, menu, event) {
                        prompt_dialog({
                            text: item.customer + "：" + item.complaint,
                            label: "理由",
                            icon: 'comment',
                            action_text: menu.name,
                            action_style: menu.style,
                            action: function action(detail) {
                                _.set_busy();
                                api.ticket_waive(item.id, detail, function (err) {
                                    if (err != null) {
                                        _.set_idle();
                                        popup(err);
                                    } else {
                                        _.set_idle();
                                        popup("已放弃 " + item.customer + " 的工单：" + detail);
                                        item.result = '放弃';
                                        item.detail = detail;
                                    }
                                    $scope.$apply();
                                }, _.list_refresh);
                            }
                        }, event.target);
                    }
                }]
            }, {
                name: '工单',
                icon: 'receipt',
                view: 'list',
                role: '售后部信息员',
                list: api.ticket_list_latest(),
                list_text: {
                    major: function major(item) {
                        return item.customer;
                    },
                    desc: function desc(item) {
                        return item.complaint;
                    },
                    minor: function minor(item) {
                        return item.result == null ? "" : item.result;
                    },
                    menu: function menu(item) {
                        return item.detail;
                    },
                    style: function style(item) {
                        return item.result == null ? '' : item.result === '成功' ? 'color-yes' : item.result === '失败' ? 'color-no2' : 'color-no';
                    }
                },
                add: {
                    inputs: [{
                        id: 'customer',
                        label: "客户称呼",
                        type: "text",
                        icon: 'account-clipboard'
                    }, {
                        id: 'contact',
                        label: "联系方式",
                        type: "text",
                        icon: 'motorbike'
                    }, {
                        id: 'complaint',
                        label: "详情概述",
                        type: "text",
                        icon: 'comment-more'
                    }],
                    action: function action(values, close) {
                        _.set_busy();
                        api.ticket_add(values.complaint, values.customer, values.contact, function (err) {
                            if (err != null) {
                                _.set_idle();
                                popup(err);
                            } else {
                                _.set_idle();
                                popup(values.customer + " 的工单添加成功");
                                close();
                                _.list_refresh();
                            }
                            $scope.$apply();
                        });
                    }
                },
                menu: [{
                    name: '指派业务员',
                    icon: 'account-network',
                    style: 'md-primary',
                    click: function click(item) {
                        var ticket = item.id;
                        _.page_select({
                            name: '可指派业务员',
                            view: 'list',
                            list: api.ticket_list_free(),
                            list_text: {
                                major: function major(item) {
                                    return item.name;
                                },
                                desc: function desc(item) {
                                    return '';
                                },
                                minor: function minor(item) {
                                    return '';
                                },
                                menu: function menu(item) {
                                    return item.name;
                                }
                            },
                            back: true,
                            menu: [{
                                name: '指派',
                                icon: 'account-network',
                                style: 'md-primary',
                                click: function click(item) {
                                    console.log(ticket, item);
                                    _.set_busy();
                                    api.ticket_assign(ticket, item.id, function (err) {
                                        if (err != null) {
                                            _.set_idle();
                                            popup(err);
                                        } else {
                                            _.set_idle();
                                            popup(item.name + " 已指派");
                                            _.page_back();
                                            _.list_refresh();
                                        }
                                        $scope.$apply();
                                    }, _.list_refresh);
                                }
                            }]
                        });
                    }
                }]
            }];

            _.page_select = function (page) {
                if (_.page === page) return;
                if (_.page != null && _.page.fini) _.page.fini(_.page);
                if (page.init) page.init(page);
                page.old_page = _.page;
                _.view = page.view;
                _.page = page;
                _.close_menu();
            };
            _.page_select(_.pages[0]);

            _.page_is = function (page) {
                return _.page === page;
            };

            _.view_is = function (view) {
                return _.view === view;
            };

            _.page_back = function () {
                if (_.page.old_page == null) return;
                _.page_select(_.page.old_page);
            };

            _.accessible = function (page) {
                if (page.role == null) return true;
                return page.role === _.role;
            };

            _.toolbar_icons = [{
                name: '修改我的密码',
                icon: 'account-key',
                style: '',
                style_collapse: 'md-primary',
                prompt: {
                    label: "新密码",
                    type: "password",
                    text: _.role + " " + _.name + " " + _.username,
                    action: function action(pwd) {
                        _.set_busy();
                        api.user_password_self(pwd, function (err) {
                            if (err != null) {
                                _.set_idle();
                                popup(err);
                            } else {
                                _.set_busy("密码修改成功。需要重新登录。");
                                setTimeout(api.login, 4000);
                            }
                            $scope.$apply();
                        });
                    }
                }
            }, {
                divider: true
            }, {
                name: '登出所有设备',
                icon: 'account-alert',
                style: 'md-warn md-hue-3',
                style_collapse: 'md-warn',
                click: function click() {
                    _.set_busy();
                    api.user_revoke_self(api.login);
                }
            }, {
                name: '切换用户',
                icon: 'account-switch',
                style: '',
                style_collapse: '',
                click: function click() {
                    _.set_busy();
                    api.login();
                }
            }];
            _.toolbar_icon_click = function (icon, event) {
                if (icon.view != null) _.page_select(icon);else if (icon.prompt != null) {
                    icon.prompt.action_text = icon.name;
                    icon.prompt.icon = icon.icon;
                    prompt_dialog(icon.prompt, event.target);
                } else icon.click(event);
            };

            _.loading = false;
            var list = {};
            _.list_refresh = function () {
                list = {
                    api: list.api,
                    next: list.api(),
                    cache: [],
                    done: false
                };
            };
            var list_load = function list_load() {
                var lst = list;
                if (lst.done) return;
                if (_.loading) return;
                _.loading = true;
                if (!$scope.$$phase) $scope.$apply();
                lst.next(function (items) {
                    if (items == null) {
                        popup('载入失败');
                        lst.done = true;
                    } else {
                        if (items.length < 10) lst.done = true;
                        items.forEach(function (item) {
                            lst.cache.push(item);
                        });
                    }
                    _.loading = false;
                    $scope.$apply();
                });
            };
            var items = {};
            items.getItemAtIndex = function (i) {
                if (i < list.cache.length) return list.cache[i];
                list_load();
            };
            items.getLength = function () {
                return list.cache.length + (list.done ? 0 : 1);
            };
            _.list_items = function () {
                if (_.page.list == null) return;
                if (list.api !== _.page.list) {
                    list.api = _.page.list;
                    _.list_refresh();
                }
                return items;
            };
            _.list_add = function (ev, page) {
                $mdDialog.show({
                    templateUrl: 'form-dialog',
                    clickOutsideToClose: true,
                    controller: 'form-dialog-controller as dialog',
                    openFrom: ev.target,
                    locals: { info: page.add, action_text: "添加" + page.name }
                });
            };
        });

        app.controller('prompt-dialog-controller', function ($mdDialog, info) {
            var _ = this;

            Object.assign(_, info);
            if (_.action_style == null) _.action_style = "md-primary";
            if (_.type == null) _.type = "text";

            _.close = function () {
                $mdDialog.hide();
            };

            _.action = function () {
                info.action(_.value);
                _.close();
            };
        });

        app.controller('form-dialog-controller', function ($mdDialog, info, action_text) {
            var _ = this;

            _.inputs = info.inputs;
            _.values = {};
            _.action_text = action_text;

            _.close = function () {
                $mdDialog.hide();
            };

            _.action = function () {
                info.action(_.values, _.close);
            };
        });
    };
});

require.config({
    waitSeconds: 0,
    paths: {
        'angular': 'angular/angular.min',
        'angular-aria': 'angular/angular-aria.min',
        'angular-animate': 'angular/angular-animate.min',
        'angular-messages': 'angular/angular-messages.min',
        'angular-material': 'angular/angular-material.min'
    },
    shim: {
        'angular': { 'exports': 'angular' },
        'angular-aria': ['angular'],
        'angular-animate': ['angular'],
        'angular-messages': ['angular'],
        'angular-material': ['angular', 'angular-aria', 'angular-animate', 'angular-messages']
    },
    priority: ['api', 'app', 'angular']
});

// two-staged require
// so that login check will be earlier
require(['api', 'app'], function (make_api, app) {
    // init api
    var api = make_api();
    if (api == null) return;
    api.user_validate(function () {}); // validate user so that user's time may not be wasted.

    require(['angular', 'angular-material'], function (angular) {
        // fuck ARIA off
        (function (warn) {
            return console.warn = function (x) {
                return x.startsWith('ARIA:') ? null : warn(x);
            };
        })(console.warn);

        // init app
        angular.element(document.querySelector('html')).ready(function () {
            app(angular, api);
            angular.bootstrap(document, ['rsms']);

            // hide preloading
            var preloading = document.querySelector('#preloading');
            preloading.parentElement.removeChild(preloading);
        });

        // TODO: remove these on release
        window.api = api;
    });
});

define("init", function () {});

